<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>第二章：Models模块</title>
    <link rel="stylesheet" href="../shared_styles.css">
</head>
<body>
    <div class="container">
        <h1>第二章：Models模块</h1>

        <section id="2-1">
            <h2>2.1 语言模型（LLMs）概述</h2>
            <p>在Langchain中，"Models"（模型）是与各种语言模型进行交互的核心接口。这些模型是驱动Langchain应用智能的引擎。Langchain致力于提供一个通用接口，使得开发者可以方便地在不同的模型之间切换，而无需重写大量代码。</p>
            <p>Langchain主要支持三种类型的模型：</p>
            <ul>
                <li><strong>LLMs (Large Language Models - 大型语言模型):</strong> 这是最通用的模型类型，它们接受一个文本字符串作为输入，并返回一个文本字符串作为输出。例如，OpenAI的<code>gpt-3.5-turbo-instruct</code>就是一个LLM。</li>
                <li><strong>Chat Models (聊天模型):</strong> 这类模型通常基于LLM构建，但其接口更适合对话场景。它们接受一个聊天消息列表作为输入，并返回一个AI聊天消息作为输出。聊天消息可以是用户消息、AI消息、系统消息或函数调用消息。例如，OpenAI的<code>gpt-4</code>或<code>gpt-3.5-turbo</code>通常通过聊天模型接口使用。</li>
                <li><strong>Text Embedding Models (文本嵌入模型):</strong> 这类模型接受文本作为输入，并返回一个浮点数列表（即向量嵌入），这些向量能够捕捉文本的语义信息。嵌入对于文本相似性比较、语义搜索、聚类等任务非常有用。例如，OpenAI的<code>text-embedding-ada-002</code>。</li>
            </ul>
            <p>Models模块的目标是为这些不同类型的模型提供一致的、易于使用的API。</p>
        </section>

        <section id="2-2">
            <h2>2.2 如何集成不同的语言模型（OpenAI, Hugging Face等）</h2>
            <p>Langchain通过其丰富的集成库支持多种模型提供商。每个提供商通常有其对应的包需要安装。</p>

            <h3>2.2.1 阿里云DashScope Qwen模型 (通过OpenAI兼容接口)</h3>
            <p>阿里云DashScope提供了通义千问（Qwen）系列模型，并且其API与OpenAI的API格式兼容。这意味着我们可以继续使用<code>langchain-openai</code>库来接入这些模型。首先，确保你已安装<code>langchain-openai</code>包：</p>
            <pre><code>pip install langchain-openai</code></pre>
            <p>然后，你需要设置你的DashScope API密钥，通常是通过环境变量<code>DASHSCOPE_API_KEY</code>。</p>
            <p><strong>LLM/Chat Model示例 (DashScope Qwen):</strong><br/>
            DashScope的Qwen模型主要通过类聊天模型接口提供服务，因此我们统一使用<code>ChatOpenAI</code>进行配置。</p>
            <pre><code class="language-python">
from langchain_openai import ChatOpenAI # 使用ChatOpenAI类
from langchain_core.messages import HumanMessage, SystemMessage
from dotenv import load_dotenv
import os

load_dotenv() # 加载 .env 文件中的环境变量
api_key = os.environ.get("DASHSCOPE_API_KEY")
if not api_key:
    raise ValueError("DASHSCOPE_API_KEY not found in environment variables.")

# 初始化配置为DashScope Qwen的ChatOpenAI实例
chat_model = ChatOpenAI(
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    api_key=api_key,
    model="qwen-plus", # 例如使用通义千问增强版 qwen-plus 或 qwen-turbo
    temperature=0.7
)

# LLM式调用 (单个文本输入)
prompt_text = "请写一首关于春天的诗。"
# ChatOpenAI期望消息列表，所以我们将单个提示包装成HumanMessage
response_llm_style = chat_model.invoke([HumanMessage(content=prompt_text)])
print("LLM-style response:")
print(response_llm_style.content)

# Chat Model式调用 (消息列表输入)
messages = [
    SystemMessage(content="你是一个乐于助人的AI助手。"),
    HumanMessage(content="你好，我想了解一下Langchain。")
]
response_chat_style = chat_model.invoke(messages)
print("\nChat-style response:")
print(response_chat_style.content)
            </code></pre>
            <p><strong>Text Embedding Model示例 (DashScope):</strong><br/>
            对于DashScope的文本嵌入服务，推荐使用Langchain社区提供的专用<code>DashScopeEmbeddings</code>类，以确保最佳兼容性。</p>
            <pre><code class="language-python">
from langchain_community.embeddings import DashScopeEmbeddings # 使用DashScope专用嵌入类
from dotenv import load_dotenv
import os

load_dotenv()
api_key = os.environ.get("DASHSCOPE_API_KEY")
if not api_key:
    raise ValueError("DASHSCOPE_API_KEY not found in environment variables.")

# 初始化DashScopeEmbeddings
embeddings_generator = DashScopeEmbeddings(
    dashscope_api_key=api_key,
    model="text-embedding-v1" # DashScope文本嵌入模型，例如 text-embedding-v1 或 text-embedding-v2
)
embedding_vector = embeddings_generator.embed_query("你好，世界！")
print(embedding_vector[:5]) # 打印向量的前5个维度
document_embeddings = embeddings_generator.embed_documents(["文档1的内容", "文档2的内容"])
print(f"嵌入了 {len(document_embeddings)} 个文档。")
            </code></pre>
            <div class="note">
                <p>请确保已安装 <code>langchain-community</code> 包 (<code>pip install langchain-community</code>)。<code>DashScopeEmbeddings</code> 类会直接与DashScope的API交互，处理其特定的请求格式。</p>
            </div>
            <div class="note">
                <p><strong>重要提示:</strong> 使用阿里云DashScope时，请确保你选择的模型 (<code>model</code>参数) 在DashScope平台上可用，并且你的API密钥具有访问该模型的权限。模型名称可能与OpenAI的原始模型名称不同。请查阅DashScope的官方文档获取可用的模型列表和正确的模型标识符（如<code>qwen-turbo</code>, <code>qwen-plus</code>, <code>qwen-max</code>, <code>text-embedding-v1</code>等）。</p>
            </div>
            <h3>2.2.2 Hugging Face 模型</h3>
            <p>Hugging Face Hub 提供了大量开源模型。你可以通过<code>langchain-huggingface</code>包来集成它们。</p>
            <pre><code>pip install langchain-huggingface transformers sentence-transformers</code></pre>
            <p>你可能需要一个Hugging Face API Token (<code>HUGGINGFACEHUB_API_TOKEN</code>环境变量) 来访问某些模型或提高速率限制。</p>
            <p><strong>LLM示例 (Hugging Face Hub):</strong></p>
            <pre><code class="language-python">
# from langchain_huggingface import HuggingFaceEndpoint
# from dotenv import load_dotenv
# import os

# load_dotenv() # 确保 HUGGINGFACEHUB_API_TOKEN 在环境变量中

# 例如使用 Google 的 Gemma 模型
# 你需要提供模型的 endpoint URL，这通常在 Hugging Face Inference Endpoints 服务中创建
# 或者使用 HuggingFaceHub 类直接引用模型仓库ID (可能需要付费API或本地运行)
# 此处假设你有一个可用的 endpoint URL
# repo_id = "google/gemma-7b-it"
# llm = HuggingFaceEndpoint(
#     endpoint_url=os.getenv("HF_INFERENCE_ENDPOINT_URL_GEMMA_7B_IT"), # 你需要替换成你的endpoint
#     huggingfacehub_api_token=os.getenv("HUGGINGFACEHUB_API_TOKEN"),
#     task="text-generation",
#     max_new_tokens=200
# )
# response = llm.invoke("请解释什么是大型语言模型。")
# print(response)
# 注意：直接运行 HuggingFaceEndpoint 可能需要付费服务或自行部署。
# 另一种方式是使用 HuggingFacePipeline 加载本地模型或社区模型。
            </code></pre>
             <div class="note">
                <p><strong>注意:</strong> 使用Hugging Face Hub上的模型时，特别是通过<code>HuggingFaceEndpoint</code>，可能涉及到模型的可用性、API速率限制以及潜在的费用。对于本地运行或更细致的控制，<code>HuggingFacePipeline</code>是另一个常用选项，它允许你加载本地下载的模型或直接从Hub加载特定模型到本地运行。</p>
            </div>
            <pre><code class="language-python">
# LLM示例 (Hugging Face Pipeline - 本地或Hub模型)
# from langchain_huggingface import HuggingFacePipeline

# 加载一个本地模型 (你需要先下载或指定一个Hub上的模型ID)
# llm = HuggingFacePipeline.from_model_id(
#     model_id="gpt2", # 这是一个小模型，方便测试
#     task="text-generation",
#     pipeline_kwargs={"max_new_tokens": 100},
# )
# response = llm.invoke("从前有座山，")
# print(response)
            </code></pre>
            <p><strong>Text Embedding Model示例 (Hugging Face - Sentence Transformers):</strong></p>
            <pre><code class="language-python">
from langchain_huggingface import HuggingFaceEmbeddings

# 使用 Sentence Transformers 库中的模型
embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
embedding_vector = embeddings_model.embed_query("这是一个测试句子。")
print(embedding_vector[:5])
            </code></pre>
            <h3>2.2.3 Google Gemini 模型</h3>
            <p>安装Google Generative AI SDK：</p>
            <pre><code>pip install langchain-google-genai</code></pre>
            <pre><code class="language-python">from langchain_google_genai import ChatGoogleGenerativeAI
import os

os.environ["GOOGLE_API_KEY"] = "your_api_key"

model = ChatGoogleGenerativeAI(model="gemini-pro")
response = model.invoke("Langchain是什么？")
print(response.content)</code></pre>

            <h3>2.2.4 Anthropic Claude 3 模型</h3>
            <pre><code>pip install langchain-anthropic</code></pre>
            <pre><code class="language-python">from langchain_anthropic import ChatAnthropic

model = ChatAnthropic(model_name="claude-3-opus-20240229", max_tokens=1024)
response = model.invoke("请解释强化学习的基本概念")
print(response.content)</code></pre>

            <h3>2.2.5 模型选择指南</h3>
            <table class="model-selection">
                <tr>
                    <th>应用场景</th>
                    <th>推荐模型</th>
                    <th>特点</th>
                </tr>
                <tr>
                    <td>通用对话</td>
                    <td>Qwen-Turbo<br>Claude 3 Haiku</td>
                    <td>响应快，成本低</td>
                </tr>
                <tr>
                    <td>复杂推理</td>
                    <td>Qwen-Max<br>Claude 3 Opus</td>
                    <td>强推理能力</td>
                </tr>
                <tr>
                    <td>多语言支持</td>
                    <td>Gemini Pro</td>
                    <td>覆盖100+语言</td>
                </tr>
                <tr>
                    <td>本地部署</td>
                    <td>Llama 3<br>Qwen1.5</td>
                    <td>数据隐私保障</td>
                </tr>
            </table>
        </section>

        <section id="2-3">
            <h2>2.3 模型的调用和配置（温度、最大Token等）</h2>
            <p>在初始化或调用模型时，你可以传递各种参数来控制模型的行为和输出。当通过<code>langchain-openai</code>配置阿里云DashScope等兼容OpenAI API的服务时，许多参数名和行为是相似的。</p>
            <ul>
                <li><strong><code>model</code> (或 <code>model_name</code>):</strong> 指定要使用的具体模型标识符。例如，对于DashScope Qwen，可能是 <code>"qwen-plus"</code>, <code>"qwen-turbo"</code>, 或嵌入模型 <code>"text-embedding-v1"</code>。</li>
                <li><strong><code>temperature</code> (温度):</strong> 一个介于0和1（或更高，取决于模型）之间的浮点数。它控制输出的随机性。较低的温度（如0.2）使输出更具确定性、更保守；较高的温度（如0.8）使输出更具创造性、更多样化，但也可能导致不相关或不准确的内容。</li>
                <li><strong><code>max_tokens</code> (或 <code>max_new_tokens</code>):</strong> 控制模型生成的最大token数量。这有助于限制输出长度和控制成本（因为许多API按token计费）。</li>
                <li><strong><code>top_p</code> (Nucleus Sampling):</strong> 另一种控制输出随机性的方法。模型只考虑累积概率超过<code>top_p</code>阈值的最小token集合。通常不与<code>temperature</code>同时使用（或将一个设为默认值）。</li>
                <li><strong>Streaming (流式输出):</strong> 对于LLMs和Chat Models，可以启用流式输出，这样模型会逐步返回生成的token，而不是等待整个响应完成后一次性返回。这对于交互式应用非常有用。
                    <pre><code class="language-python">
# Chat Model 流式输出示例 (配置为DashScope)
# from langchain_openai import ChatOpenAI
# from langchain_core.messages import HumanMessage
# import os
# from dotenv import load_dotenv
# load_dotenv()
# api_key = os.environ.get("DASHSCOPE_API_KEY")
# if api_key:
#     chat = ChatOpenAI(
#         base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
#         api_key=api_key,
#         model="qwen-turbo", # DashScope模型
#         streaming=True
#     )
#     for chunk in chat.stream([HumanMessage(content="给我讲个简短的笑话。")]):
#         print(chunk.content, end="", flush=True)
                    </code></pre>
                </li>
            <li><strong><code>api_key</code>:</strong> 用于指定API密钥。</li>
            <li><strong><code>base_url</code>:</strong> 用于指定API服务的基础URL。</li>
            <li><strong>批量处理 (batch):</strong> 同时处理多个输入提升效率
                <pre><code class="language-python"># 批量处理示例
responses = chat_model.batch([
    [HumanMessage(content="问题1")],
    [HumanMessage(content="问题2")]
])
for res in responses:
    print(res.content)</code></pre>
            </li>
            <li><strong>异步调用 (async):</strong> 非阻塞式调用提升响应速度
                <pre><code class="language-python"># 异步调用示例
async def async_query():
    response = await chat_model.ainvoke([HumanMessage(content="异步问题")])
    print(response.content)

import asyncio
asyncio.run(async_query())</code></pre>
            </li>
            <li><strong>错误重试机制:</strong> 使用tenacity库自动重试
                <pre><code class="language-python">from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def safe_invoke(prompt):
    return chat_model.invoke([HumanMessage(content=prompt)])</code></pre>
            </li>
            <li><strong>其他特定于模型的参数:</strong> 如停止序列 (<code>stop</code>)等。</li>
            </ul>
            <p>Langchain的模型类通常会在初始化时接受这些参数。例如，配置DashScope Qwen：</p>
            <pre><code class="language-python">
from langchain_openai import ChatOpenAI # 使用ChatOpenAI接入
import os
# from dotenv import load_dotenv # 假设已加载

# load_dotenv()
# api_key = os.environ.get("DASHSCOPE_API_KEY")
# if not api_key:
#     raise ValueError("DASHSCOPE_API_KEY not found.")

# llm = ChatOpenAI(
#     base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
#     api_key=api_key,
#     model="qwen-plus",
#     temperature=0.5,
#     max_tokens=150
# )
            </code></pre>
            <p class="note">注意：上面的代码片段仅为示例，实际使用时请确保<code>api_key</code>已正确加载。</p>
        </section>

        <section id="2-4">
            <h2>2.4 聊天模型（Chat Models）</h2>
            <p>聊天模型是专门为对话优化的。它们的核心API与LLM略有不同：</p>
            <ul>
                <li><strong>输入:</strong> 接受一个<code>List[BaseMessage]</code>（消息列表）。</li>
                <li><strong>输出:</strong> 返回一个<code>AIMessage</code>（AI消息对象），其中包含AI的回复。</li>
            </ul>
            <p>Langchain定义了几种基本消息类型 (<code>BaseMessage</code>的子类):</p>
            <ul>
                <li><strong><code>HumanMessage</code>:</strong> 代表用户的消息。<code>content</code>属性是文本内容。</li>
                <li><strong><code>AIMessage</code>:</strong> 代表AI助手的消息。<code>content</code>属性是文本内容。它还可以包含<code>tool_calls</code>等用于Agent和Tool使用的信息。</li>
                <li><strong><code>SystemMessage</code>:</strong> 代表系统指令，用于设定AI的角色、行为准则或提供高级别的对话上下文。通常放在消息列表的开头。</li>
                <li><strong><code>ToolMessage</code> (之前叫 <code>FunctionMessage</code>):</strong> 代表工具调用的结果。当AI决定调用一个工具后，应用执行该工具，然后将结果以<code>ToolMessage</code>的形式返回给AI，以便它继续生成下一步响应。</li>
            </ul>
            <p>一个典型的聊天交互流程（使用DashScope Qwen模型）：</p>
            <pre><code class="language-python">
from langchain_openai import ChatOpenAI # 使用ChatOpenAI接入
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from dotenv import load_dotenv
import os

load_dotenv()
api_key = os.environ.get("DASHSCOPE_API_KEY")
if not api_key:
    raise ValueError("DASHSCOPE_API_KEY not found.")

chat_model = ChatOpenAI(
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    api_key=api_key,
    model="qwen-plus" # DashScope Qwen模型
)

history = [
    SystemMessage(content="你是一个知识渊博的历史学家。"),
    HumanMessage(content="你好，请问古埃及文明有哪些主要成就？")
]

ai_response = chat_model.invoke(history)
print(f"AI: {ai_response.content}")

# 更新历史记录，准备下一次交互
history.append(ai_response)
history.append(HumanMessage(content="这些成就对后世有何影响？"))

ai_response_2 = chat_model.invoke(history)
print(f"AI: {ai_response_2.content}")
            </code></pre>
            <p>聊天模型是构建聊天机器人、问答系统以及需要多轮对话的应用的基础。</p>
        </section>

        <section id="2-5">
            <h2>2.5 文本嵌入模型（Text Embedding Models）</h2>
            <p>文本嵌入模型将文本转换为密集的数值向量。这些向量（嵌入）在向量空间中的距离可以表示文本之间的语义相似度。</p>
            <p>Langchain中的嵌入模型类通常提供两个核心方法：</p>
            <ul>
                <li><strong><code>embed_query(text: str) -> List[float]</code>:</strong> 为单个文本（通常是用户查询）生成嵌入。</li>
                <li><strong><code>embed_documents(texts: List[str]) -> List[List[float]]</code>:</strong> 为一个文本列表（通常是文档集合）生成嵌入，返回一个嵌入列表。</li>
            </ul>
            <p>这些嵌入是构建语义搜索、推荐系统、RAG（检索增强生成）等应用的关键组件。以下是如何使用DashScope的文本嵌入模型（推荐使用专用的<code>DashScopeEmbeddings</code>类）：</p>
            <pre><code class="language-python">
from langchain_community.embeddings import DashScopeEmbeddings # 使用DashScope专用嵌入类
from dotenv import load_dotenv
import os

load_dotenv()
api_key = os.environ.get("DASHSCOPE_API_KEY")
if not api_key:
    raise ValueError("DASHSCOPE_API_KEY not found in environment variables.")

# 初始化DashScopeEmbeddings
embeddings_generator = DashScopeEmbeddings(
    dashscope_api_key=api_key,
    model="text-embedding-v1" # DashScope文本嵌入模型，例如 text-embedding-v1 或 text-embedding-v2
)

query_text = "什么是人工智能？"
document_texts = [
    "人工智能是计算机科学的一个分支。",
    "机器学习是实现人工智能的一种方法。",
    "深度学习是机器学习的一个子领域。",
    "今天天气真好。"
]

query_embedding = embeddings_generator.embed_query(query_text)
document_embeddings = embeddings_generator.embed_documents(document_texts)

print(f"查询嵌入的维度: {len(query_embedding)}")
print(f"文档嵌入的数量: {len(document_embeddings)}")
print(f"第一个文档嵌入的维度: {len(document_embeddings[0])}")

# 之后可以使用这些嵌入进行相似度计算 (例如余弦相似度)
# from sklearn.metrics.pairwise import cosine_similarity
# import numpy as np
# similarity = cosine_similarity(np.array(query_embedding).reshape(1, -1), np.array(document_embeddings))
# print(f"查询与各文档的相似度: {similarity}")
            </code></pre>
            <div class="note">
                <p>请确保已安装 <code>langchain-community</code> 包 (<code>pip install langchain-community</code>)。<code>DashScopeEmbeddings</code> 类会直接与DashScope的API交互，处理其特定的请求格式。</p>
            </div>
            <p>选择合适的嵌入模型取决于你的具体需求，包括性能、成本、嵌入维度以及模型在特定领域数据的表现。</p>
        </section>

        <section id="2-6">
            <h2>2.6 本地模型部署与调用</h2>
            <p>对于需要数据隐私或离线使用的场景，可部署本地模型：</p>
            
            <h3>2.6.1 使用Ollama部署本地模型</h3>
            <p>安装Ollama：</p>
            <pre><code># Linux安装
curl -fsSL https://ollama.com/install.sh | sh

# 下载模型
ollama pull llama3
ollama pull qwen:7b</code></pre>
            
            <h3>2.6.2 Langchain调用本地模型</h3>
            <pre><code class="language-python">from langchain_community.llms import Ollama

# 调用本地Llama3
llama = Ollama(model="llama3")
print(llama.invoke("Langchain是什么？"))

# 调用本地Qwen
qwen = Ollama(model="qwen:7b")
print(qwen.invoke("请解释深度学习"))</code></pre>
            
            <div class="note">
                <p><strong>性能优化提示：</strong></p>
                <ul>
                    <li>使用GPU加速：确保系统有NVIDIA GPU并安装CUDA</li>
                    <li>量化模型：使用4-bit量化减少显存占用</li>
                    <li>批处理：一次处理多个请求提升吞吐量</li>
                </ul>
            </div>
        </section>

        <div class="navigation">
            <a href="../langchain_tutorial_outline.html">返回目录</a>
            <a href="../chapter1/chapter1_basics.html">上一章：Langchain基础</a>
            <a href="../chapter3/chapter3_prompts.html">下一章：Prompts模块</a>
        </div>
    </div>
</body>
</html>
